# -*- coding: utf-8 -*-
"""
author:xiang
date:2023-02-11
desc:
sample:
"""
import json
import re
import time
import struct

import jsonpath as jsonpath
import requests
import umsgpack
from websocket._core import create_connection
from websocket._exceptions import WebSocketTimeoutException

from utils.assertUtils import assert_equals
import logging
import subprocess


class RequestUtil(requests.Session):
    def __init__(self):
        super().__init__()
        self.var_pool_data = {}  # 定义变量池
        self.result = {}  # 返回结果
        self.retry_num = 0  # 定义初始次数
        # logging.basicConfig(level=logging.INFO)

    def create_conn(self, url, data, case_desc=None, verification=None, extract=None, **kwargs):
        global ws
        before_time = time.time()
        for k, v in data.items():
            data[k] = self.replace_template_str(v)
            try:
                data[k] = json.loads(v)
            except:
                pass
        logging.info('*** requests front ***'.center(80, "-"))
        logging.info(f"case desc：{case_desc}")
        logging.info(f"数据提取规则： {extract}")
        logging.info(f"Socket断言表达式： {verification}")
        logging.info(f'Socket.url : {url}')
        for k, v in data.items():
            logging.info(f'requests.data : {k}：{v}')
        ws = create_connection(url, timeout=5)
        ws.send_binary(umsgpack.packb(data))
        ws.send_binary('\r\r\n')
        recv_num = 0
        while recv_num < 10:
            response = ''
            rec = self.rec_response()
            response = self.res_decode(rec)
            recv_num += 1
            try:
                if response['m'] == 10202:
                    break
            except Exception:
                logging.info("接口请求失败，重试完毕！")
        logging.info(f"接口请求失败，进行第 {self.retry_num + 1} 次重试")
        time.sleep(1)
        after_time = time.time()
        self.result['took_time'] = round((after_time - before_time) * 1000)
        logging.info('*** response behind ***'.center(80, "-"))
        logging.info(f'response.status_code : {self.get_status()}')
        logging.info(f'response : {response}')
        self.result['response'] = response
        if verification:
            if verification.endswith(";"):
                verification = verification.split(";")[0]
            for ver in verification.split(";"):
                expr = ver.split("=")[0]
                # 判断Jsonpath还是正则断言
                if expr.startswith("$."):
                    actual = jsonpath.jsonpath(response.json(), expr)
                    if not actual:
                        logging.error("该jsonpath未匹配到值,请确认接口响应和jsonpath正确性")
                    actual = actual[0]
                else:
                    actual = re.findall(expr, response.text)[0]
                expect = ver.split("=")[1]
                try:
                    assert_equals(str(actual), expect)
                    self.result['check_result'] = '检查成功'
                except:
                    self.result['check_result'] = '检查失败'
        else:
            self.result['check_result'] = '跳过检查'

        if extract:
            for item in extract.split(";"):
                kvs = item.split("=")
                key = kvs[0]  # 获取关键字
                value = kvs[1]  # 获取正则表达式
                if value.startswith("$."):
                    self.save_variable(response.json(), key, jsonpath_expression=value)  # 进行JSONPATH提取并保存
                else:
                    self.save_variable(response.text, key, regular_expression=value)  # 进行正则提取并保存

        logging.info("*** requests end ***".center(80, "-"))
        return response

    # 发送协议数据，解码返回json
    def send_msg(self, data, case_desc=None, verification=None, extract=None, **kwargs):
        before_time = time.time()
        for k, v in data.items():
            data[k] = self.replace_template_str(v)
            try:
                data[k] = json.loads(v)
            except:
                pass
        logging.info('*** requests front ***'.center(80, "-"))
        logging.info(f"case desc：{case_desc}")
        logging.info(f"数据提取规则： {extract}")
        logging.info(f"Socket断言表达式： {verification}")
        for k, v in data.items():
            logging.info(f'requests.data : {k}：{v}')
        ws.send_binary(umsgpack.packb(data))
        ws.send_binary('\r\r\n')
        recv_num = 0
        while recv_num < 10:
            response = ''
            rec = self.rec_response()
            response = self.res_decode(rec)
            recv_num += 1
            try:
                if response['m'] == data['c']+1:
                    break
            except Exception:
                logging.info("接口请求失败，重试完毕！")
        time.sleep(1)
        after_time = time.time()
        self.result['took_time'] = round((after_time - before_time) * 1000)
        logging.info('*** response behind ***'.center(80, "-"))
        logging.info(f'response.status_code : {self.get_status()}')
        logging.info(f'response : {response}')
        self.result['response'] = response
        if verification:
            if verification.endswith(";"):
                verification = verification.split(";")[0]
            for ver in verification.split(";"):
                expr = ver.split("=")[0]
                # 判断Jsonpath还是正则断言
                if expr.startswith("$."):
                    actual = jsonpath.jsonpath(response, expr)
                    if not actual:
                        logging.error("该jsonpath未匹配到值,请确认接口响应和jsonpath正确性")
                    actual = actual[0]
                else:
                    actual = re.findall(expr, response.text)[0]
                expect = ver.split("=")[1]
                try:
                    assert_equals(str(actual), expect)
                    self.result['check_result'] = '检查成功'
                except:
                    self.result['check_result'] = '检查失败'
        else:
            self.result['check_result'] = '跳过检查'

        if extract:
            for item in extract.split(";"):
                kvs = item.split("=")
                key = kvs[0]  # 获取关键字
                value = kvs[1]  # 获取正则表达式
                if value.startswith("$."):
                    self.save_variable(response.json(), key, jsonpath_expression=value)  # 进行JSONPATH提取并保存
                else:
                    self.save_variable(response.text, key, regular_expression=value)  # 进行正则提取并保存

        logging.info("*** requests end ***".center(80, "-"))
        return response

    def send_request(self, method, url, case_desc=None, verification=None, extract=None, **kwargs):
        self.result = {}
        # self.retry_num = 0
        before_time = time.time()
        for k, v in kwargs.items():
            kwargs[k] = self.replace_template_str(v)
            try:
                kwargs[k] = json.loads(v)
            except:
                pass
        logging.info('*** requests front ***'.center(80, "-"))
        logging.info(f"case desc：{case_desc}")
        logging.info(f"数据提取规则： {extract}")
        logging.info(f"Api 断言表达式： {verification}")
        logging.info(f'requests.method : {method}')
        logging.info(f'requests.url : {url}')
        for k, v in kwargs.items():
            logging.info(f'requests.data : {k}：{v}')
        try:
            response = super().request(method, url, **kwargs)
            response.encoding = 'utf-8'
        except Exception as e:
            logging.error(f"请求发生异常: {e}")
            self.result['response'] = f"接口请求失败:{e}"
            return
        after_time = time.time()

        if response.status_code == 200:
            self.result['took_time'] = round((after_time - before_time) * 1000)
            logging.info(f"花费时间: {self.result['took_time']}ms\n")
        # elif response.status_code in (500, 502, 503, 504):
        #     self.result['response'] = f'接口请求失败: {response.status_code}'
        #     return
        else:
            if self.retry_num > 2:
                # self.retry_num = 0
                logging.info("接口请求失败，重试完毕！")
                self.result['response'] = f'接口请求失败: {response.status_code}'
                return
            logging.info(f"接口请求失败，响应code为 {response.status_code}, 进行第 {self.retry_num + 1} 次重试")
            time.sleep(1)
            self.retry_num += 1
            return self.send_request(method, url, **kwargs)

        logging.info('*** response behind ***'.center(80, "-"))
        logging.info(f'response.status_code : {response.status_code}')
        logging.info(f'response.headers : {response.headers}')
        logging.info(f'response.text : {response.text}')
        self.result['response'] = response.text
        if verification:
            if verification.endswith(";"):
                verification = verification.split(";")[0]
            for ver in verification.split(";"):
                expr = ver.split("=")[0]
                # 判断Jsonpath还是正则断言
                if expr.startswith("$."):
                    actual = jsonpath.jsonpath(response.json(), expr)
                    if not actual:
                        logging.error("该jsonpath未匹配到值,请确认接口响应和jsonpath正确性")
                    actual = actual[0]
                else:
                    actual = re.findall(expr, response.text)[0]
                expect = ver.split("=")[1]
                try:
                    assert_equals(str(actual), expect)
                    self.result['check_result'] = '检查成功'
                except:
                    self.result['check_result'] = '检查失败'
        else:
            self.result['check_result'] = '跳过检查'

        if extract:
            for item in extract.split(";"):
                kvs = item.split("=")
                key = kvs[0]  # 获取关键字
                value = kvs[1]  # 获取正则表达式
                if value.startswith("$."):
                    self.save_variable(response.json(), key, jsonpath_expression=value)  # 进行JSONPATH提取并保存
                else:
                    self.save_variable(response.text, key, regular_expression=value)  # 进行正则提取并保存

        logging.info("*** requests end ***".center(80, "-"))
        return response

    def execute_py(self, file_path, case_desc=None, verification=None, extract=None, **kwargs):
        before_time = time.time()
        logging.info('*** requests front ***'.center(80, "-"))
        logging.info(f"case desc：{case_desc}")
        logging.info(f"数据提取规则： {extract}")
        logging.info(f"python断言表达式： {verification}")
        logging.info(f'python.file_path : {file_path}')
        xe = subprocess.run(f"python {file_path}", stdout=subprocess.PIPE)
        response = xe.stdout.decode("gbk")
        after_time = time.time()
        self.result['took_time'] = round((after_time - before_time) * 1000)
        logging.info('*** response behind ***'.center(80, "-"))
        logging.info(f'response : {response}')
        self.result['response'] = response
        if verification:
            if verification:
                if verification in response:
                    self.result['check_result'] = '检查成功'
                else:
                    self.result['check_result'] = '检查失败'
        else:
            self.result['check_result'] = '跳过检查'

        if extract:
            for item in extract.split(";"):
                kvs = item.split("=")
                key = kvs[0]  # 获取关键字
                value = kvs[1]  # 获取正则表达式
                if value.startswith("$."):
                    self.save_variable(response.json(), key, jsonpath_expression=value)  # 进行JSONPATH提取并保存
                else:
                    self.save_variable(response.text, key, regular_expression=value)  # 进行正则提取并保存

        logging.info("*** requests end ***".center(80, "-"))
        return response

    def replace_template_str(self, target):
        target = str(target)
        # 正则匹配所有{{key}}，并做处理
        EXPR = r'\$\{(.*?)\}'
        keys = re.findall(EXPR, str(target))
        if keys:
            logging.info("*** processing parameters ***".center(80, "-"))
            logging.info(f"变量池中匹配到需替换的参数: {keys}")
        for key in keys:
            # value = self.variable_pool.get(key)
            value = self.var_pool_data.get(key)
            if not value:
                logging.warning("变量池中未匹配到关联参数！不进行替换操作")
                continue
            target = target.replace('${' + key + '}', str(value))
            logging.info("替换了{" + key + "} 为：" + str(value))

        # 遍历所有函数助手并执行，结束后替换
        FUNC_EXPR = r'__.*?\(.*?\)'
        funcs = re.findall(FUNC_EXPR, str(target))
        for func in funcs:
            fuc = func.split('__')[1]
            fuc_name = fuc.split("(")[0]
            fuc = fuc.replace(fuc_name, fuc_name.lower())
            value = eval(fuc)
            target = target.replace(func, str(value))
        try:
            target = eval(target)
        except:
            target = target
        return target

    def save_variable(self, target, key, jsonpath_expression=None, regular_expression=None):
        """
        存储变量到变量池
        :param target: 目标字符串
        :param key: 关键字
        :param jsonpath_expression: JSONPATH表达式
        :param regular_expression: 正则表达式
        :return:
        """
        # match_values = jsonpath.jsonpath(json.loads(target),
        #                                  jsonpath_expression) if jsonpath_expression else re.findall(
        #     regular_expression, target)
        if jsonpath_expression:
            match_values = jsonpath.jsonpath(target,jsonpath_expression)
        else:
            match_values = re.findall(regular_expression, target)

        if match_values:
            value = match_values[0]
            self.var_pool_data[key] = value
            logging.info(f"add_var_data保存了变量 {key} --> {value} 到变量池, 当前变量池参数 {self.var_pool_data}")
            return value
        else:
            logging.warning("未匹配到任何参数，不进行保存！")
            return

    def get_variable(self):
        return self.var_pool_data

    def get_result(self):
        return self.result

    def get_status(self):
        return ws.getstatus()

    def rec_response(self):
        try:
           rec = ws.recv()
           return rec
        except WebSocketTimeoutException as e:
            pass

    def res_decode(self, rec):
        try:
            data_len = len(rec) - 5
            com, data_len, data_str = struct.unpack('>1b1L%ds' % data_len, rec)
            resp = umsgpack.unpackb(data_str, raw=False)
            return resp
        except Exception as e:
            pass

    def get_status_code(self):
        pass

    def close_client(self):
        ws.close(timeout=5)
